#include "asteroidcontroller.h"
#include "gameobjectcollection.h"

#include "toolkit.h"

AsteroidController::AsteroidController(Game* game) : GameObject(game) {
    this->enableOctree = true;
    this->octree = NULL;
}

AsteroidController::~AsteroidController(void) {
}

bool AsteroidController::GetEnableOctree() const {
    return this->enableOctree;
}

void AsteroidController::SetEnableOctree(bool val) {
    this->enableOctree = val;
}

int AsteroidController::GetNumberOfAsteroidsDrawn() const {
    return this->numberOfAsteroidsDrawn;
}

void AsteroidController::Draw(float timeDelta) {
    this->numberOfAsteroidsDrawn = 0;
    if (this->enableOctree) {
        Pipeline.UpdateViewFrustum();
        vector<Octree<vector<Asteroid*>*>*>* visibleNodes = this->octree->FindVisible();
        for (vector<Octree<vector<Asteroid*>*>*>::const_iterator it = visibleNodes->begin(); it != visibleNodes->end(); ++it) {
            Octree<vector<Asteroid*>*>* node = *it;
            for (vector<Asteroid*>::const_iterator aIt = node->GetItem()->begin(); aIt != node->GetItem()->end(); ++aIt) {
                (*aIt)->Draw(timeDelta);
                this->numberOfAsteroidsDrawn++;
            }
        }

        delete visibleNodes;
    } else {
        vector<Asteroid*>* v = this->octree->GetItem();
        for (vector<Asteroid*>::const_iterator it = v->begin(); it != v->end(); ++it) {
            (*it)->Draw(timeDelta);
            this->numberOfAsteroidsDrawn++;
        }
    }
}

void AsteroidController::Initialize() {
    BoundingBox* boundingBox = new BoundingBox(Vector4(-16384, -16384, -16384, 0), Vector4(16384, 16384, 16384, 0));
    this->octree = new Octree<vector<Asteroid*>*>(boundingBox, 2048, NULL);
    this->InitializeOctree(this->octree);

    for (int i = 0; i < 700; ++i) {
        Asteroid* asteroid = new Asteroid(this->GetGame());
        Vector4 pos = Vector4(
                          Toolkit::RandomFloat(-16384, 16384),
                          Toolkit::RandomFloat(-16384, 16384),
                          Toolkit::RandomFloat(-16384, 16384),
                          0);
        asteroid->SetPosition(pos);
        asteroid->SetScale(Toolkit::RandomFloat(1, 10));
        this->Insert(this->octree, asteroid, pos);
    }

    for (vector<Asteroid*>::const_iterator it = this->octree->GetItem()->begin(); it != this->octree->GetItem()->end(); ++it) {
        (*it)->Initialize();
    }
}

void AsteroidController::InitializeOctree(Octree<vector<Asteroid*>*>* octree) {
    octree->SetItem(new vector<Asteroid*>);

    for (vector<Octree<vector<Asteroid*>*>*>::const_iterator it = octree->GetChildren()->begin(); it != octree->GetChildren()->end(); ++it) {
        this->InitializeOctree((Octree<vector<Asteroid*>*>*)*it);
    }
}

void AsteroidController::Insert(Octree<vector<Asteroid*>*>* octree, Asteroid* asteroid, Vector4 position) {
    if (octree->GetBoundingBox()->IsPointInside(position)) {
        octree->GetItem()->push_back(asteroid);

        for (vector<Octree<vector<Asteroid*>*>*>::const_iterator it = octree->GetChildren()->begin(); it != octree->GetChildren()->end(); ++it) {
            this->Insert((Octree<vector<Asteroid*>*>*)*it, asteroid, position);
        }
    }
}

void AsteroidController::LoadContent() {
    for (vector<Asteroid*>::const_iterator it = this->octree->GetItem()->begin(); it != this->octree->GetItem()->end(); ++it) {
        (*it)->LoadContent();
    }
}

void AsteroidController::Update(float timeDelta) {
    for (vector<Asteroid*>::const_iterator it = this->octree->GetItem()->begin(); it != this->octree->GetItem()->end(); ++it) {
        (*it)->Update(timeDelta);
    }
}
